C语言实现FTP文件传输

您所在的位置:网站首页 ftp上传文件命令 put C语言实现FTP文件传输

C语言实现FTP文件传输

2023-12-09 09:52| 来源: 网络整理| 查看: 265

一、要求

FTP实则为两个程序的互相交流,把信息指令相互发送并处理。 1.客户端请求下载文件 —把文件名发送给服务器。 2.服务器接收客户端发送的文件名 —根据文件名找到文件,把文件大小发送给客户端。 3.客户端接收到文件大小—准备开始接受,开辟内存,准备完成要告诉服务器可以发送了。 4.服务器接受的开始发送的指令开始发送。 5.开始接收数据,存起来,接受完成,告诉服务器接收完成。 6.关闭连接。

二、FTPclient

1. ftpclient.h

#pragma once #include #pragma comment(lib,"ws2_32.lib") // 加载静态库 #include #define SPORT 8888 // 服务器端口号 #define PACKET_SIZE (1024 - sizeof(int) * 3) // 定义标记 enum MSGTAG { MSG_FILENAME = 1, // 文件名称 服务器使用 MSG_FILESIZE = 2, // 文件大小 客户端使用 MSG_READY_READ = 3, // 准备接受 客户端使用 MSG_SENDFILE = 4, // 发送 服务器使用 MSG_SUCCESSED = 5, // 传输完成 两者都使用 MSG_OPENFILE_FAILD = 6 // 告诉客户端文件找不到 客户端使用 }; #pragma pack(1) // 设置结构体1字节对齐************** struct MsgHeader // 封装消息头 { enum MSGTAG msgID; // 当前消息标记 4 union MyUnion { struct Mystruct { int fileSize; // 文件大小 4 char fileName[256]; // 文件名 256 }fileInfo; struct { int nStart; // 包的编号 int nsize; // 该包的数据大小 char buf[PACKET_SIZE]; }packet; }; }; #pragma pack() // 初始化socket库 bool initSocket(); // 关闭socket库 bool closeSocket(); // 监听客户端连接 void connectToHost(); // 处理消息 bool processMag(SOCKET serfd); // 获取文件名 void downloadFileName(SOCKET serfd); // 文件内容读进内存 void readyread(SOCKET, struct MsgHeader*); // 写入文件内容 bool writeFile(SOCKET, struct MsgHeader*);

2. ftpclient.c

#include #include #include "ftpclient.h" char g_fileName[256]; // 保存服务器发送过来的文件名 char *g_fileBuf; // 接受存储文件内容 char g_recvBuf[1024]; // 接受消息缓冲区 int g_fileSize; // 文件总大小 int main(void) { initSocket(); connectToHost(); closeSocket(); return 0; } // 初始化socket库 bool initSocket() { WSADATA wsadata; if (0 != WSAStartup(MAKEWORD(2, 2), &wsadata)) // 启动协议,成功返回0 { printf("WSAStartup faild: %d\n", WSAGetLastError()); return false; } return true; } // 关闭socket库 bool closeSocket() { if (0 != WSACleanup()) { printf("WSACleanup faild: %d\n", WSAGetLastError()); return false; } return true; } // 监听客户端连接 void connectToHost() { // 创建server socket套接字 地址、端口号,AF_INET是IPV4 SOCKET serfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == serfd) { printf("socket faild:%d", WSAGetLastError()); return; } // 给socket绑定IP地址和端口号 struct sockaddr_in serAddr; serAddr.sin_family = AF_INET; serAddr.sin_port = htons(SPORT); // htons把本地字节序转为网络字节序 serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 服务器的IP地址 // 连接到服务器 if (0 != connect(serfd, (struct sockaddr*)&serAddr, sizeof(serAddr))) { printf("connect faild:%d", WSAGetLastError()); return; } printf("连接成功!\n"); downloadFileName(serfd); // 输入文件名 // 开始处理消息,100为发送消息间隔 while (processMag(serfd)) { //Sleep(100); } } // 处理消息 bool processMag(SOCKET serfd) { recv(serfd, g_recvBuf, 1024, 0); // 收到消息 struct MsgHeader* msg = (struct MsgHeader*)g_recvBuf; /* *MSG_FILENAME = 1, // 文件名称 服务器使用 *MSG_FILESIZE = 2, // 文件大小 客户端使用 *MSG_READY_READ = 3, // 准备接受 客户端使用 *MSG_SENDFILE = 4, // 发送 服务器使用 *MSG_SUCCESSED = 5, // 传输完成 两者都使用 *MSG_OPENFILE_FAILD = 6 // 告诉客户端文件找不到 客户端使用 */ switch (msg->msgID) { case MSG_OPENFILE_FAILD: // 6 downloadFileName(serfd); break; case MSG_FILESIZE: // 2 第一次接收 readyread(serfd,msg); break; case MSG_READY_READ: // 3 writeFile(serfd, msg); break; case MSG_SUCCESSED: // 5 printf("传输完成!\n"); closeSocket(serfd); return false; break; } return true; } void downloadFileName(SOCKET serfd) { char fileName[1024]; struct MsgHeader file; printf("输入下载的文件名:"); gets_s(fileName, 1023); // 输入文件路径 file.msgID = MSG_FILENAME; // MSG_FILENAME = 1 strcpy(file.fileInfo.fileName, fileName); send(serfd, (char*)&file, sizeof(struct MsgHeader), 0); // 发送、IP地址、内容、长度 第一次发送给服务器 } void readyread(SOCKET serfd, struct MsgHeader* pmsg) { // 准备内存 pmsg->fileInfo.fileSize g_fileSize = pmsg->fileInfo.fileSize; strcpy(g_fileName, pmsg->fileInfo.fileName); g_fileBuf = calloc(g_fileSize + 1, sizeof(char)); // 申请空间 if (g_fileBuf == NULL) { printf("申请内存失败\n"); } else { struct MsgHeader msg; // MSG_SENDFILE = 4 msg.msgID = MSG_SENDFILE; if (SOCKET_ERROR == send(serfd, (struct MsgHeader*)&msg, sizeof(struct MsgHeader), 0)) // 第二次发送 { printf("客户端 send error: %d\n", WSAGetLastError()); return; } } printf("size:%d filename:%s\n", pmsg->fileInfo.fileSize, pmsg->fileInfo.fileName); } bool writeFile(SOCKET serfd, struct MsgHeader* pmsg) { if (g_fileBuf == NULL) { return false; } int nStart = pmsg->packet.nStart; int nsize = pmsg->packet.nsize; memcpy(g_fileBuf + nStart, pmsg->packet.buf, nsize); // strncmpy一样 printf("packet size:%d %d\n", nStart + nsize, g_fileSize); if (nStart + nsize >= g_fileSize) // 判断数据是否发完数据 { FILE* pwrite; struct MsgHeader msg; pwrite = fopen(g_fileName, "wb"); msg.msgID = MSG_SUCCESSED; if (pwrite == NULL) { printf("write file error...\n"); return false; } fwrite(g_fileBuf, sizeof(char), g_fileSize, pwrite); fclose(pwrite); free(g_fileBuf); g_fileBuf = NULL; send(serfd, (char*)&msg, sizeof(struct MsgHeader), 0); return false; } return true; }

三、FTPserver

1. ftpserver.h

#pragma once #include #include #pragma comment(lib,"ws2_32.lib") // 加载静态库 #define SPORT 8888 // 服务器端口号 #define PACKET_SIZE (1024 - sizeof(int) * 3) // 定义标记 enum MSGTAG { MSG_FILENAME = 1, // 文件名称 服务器使用 MSG_FILESIZE = 2, // 文件大小 客户端使用 MSG_READY_READ = 3, // 准备接受 客户端使用 MSG_SENDFILE = 4, // 发送 服务器使用 MSG_SUCCESSED = 5, // 传输完成 两者都使用 MSG_OPENFILE_FAILD = 6 // 告诉客户端文件找不到 客户端使用 }; #pragma pack(1) // 设置结构体1字节对齐 struct MsgHeader // 封装消息头 { enum MSGTAG msgID; // 当前消息标记 4 union MyUnion { struct Mystruct { int fileSize; // 文件大小 4 char fileName[256]; // 文件名 256 }fileInfo; struct { int nStart; // 包的编号 int nsize; // 该包的数据大小 char buf[PACKET_SIZE]; }packet; }; }; #pragma pack() // 初始化socket库 bool initSocket(); // 关闭socket库 bool closeSocket(); // 监听客户端连接 void listenToClient(); // 处理消息 bool processMag(SOCKET clifd); // 读取文件,获得文件大小 bool readFile(SOCKET, struct MsgHeader*); // 发送文件 bool sendFile(SOCKET, struct MsgHeader*);

2. ftpserver.c

#include #include "ftpserver.h" char g_recvBuf[1024] = { 0 }; // 用来接收客户端消息 int g_fileSize; // 文件大小 char* g_fileBuf; // 储存文件 int main(void) { initSocket(); listenToClient(); closeSocket(); return 0; } // 初始化socket库 bool initSocket() { WSADATA wsadata; if (0 != WSAStartup(MAKEWORD(2, 2), &wsadata)) // 启动协议,成功返回0 { printf("WSAStartup faild: %d\n", WSAGetLastError()); return false; } return true; } // 关闭socket库 bool closeSocket() { if (0 != WSACleanup()) { printf("WSACleanup faild: %d\n", WSAGetLastError()); return false; } return true; } // 监听客户端连接 void listenToClient() { // 创建server socket套接字 地址、端口号,AF_INET是IPV4 SOCKET serfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (INVALID_SOCKET == serfd) { printf("socket faild:%d", WSAGetLastError()); return; } // 给socket绑定IP地址和端口号 struct sockaddr_in serAddr; serAddr.sin_family = AF_INET; serAddr.sin_port = htons(SPORT); // htons把本地字节序转为网络字节序 serAddr.sin_addr.S_un.S_addr = ADDR_ANY; // 监听本机所有网卡 if (0 != bind(serfd, (struct sockaddr*)&serAddr, sizeof(serAddr))) { printf("bind faild:%d", WSAGetLastError()); return; } // 监听客户端连接 if (0 != listen(serfd, 10)) // 10为队列最大数 { printf("listen faild:%d", WSAGetLastError()); return; } // 有客户端连接,接受连接 struct sockaddr_in cliAddr; int len = sizeof(cliAddr); SOCKET clifd = accept(serfd, (struct sockaddr*)&cliAddr, &len); if (INVALID_SOCKET == clifd) { printf("accept faild:%d", WSAGetLastError()); return; } printf("接受成功!\n"); // 开始处理消息 while (processMag(clifd)) { Sleep(100); } } // 处理消息 bool processMag(SOCKET clifd) { // 成功接收消息返回收到的字节数,否则返回0 int nRes = recv(clifd, g_recvBuf, 1024, 0); // 接收 if (nRes msgID) { case MSG_FILENAME: // 1 第一次接收 printf("%s\n", msg->fileInfo.fileName); readFile(clifd,msg); break; case MSG_SENDFILE: // 4 sendFile(clifd,msg); break; case MSG_SUCCESSED: // 5 exitmsg.msgID = MSG_SUCCESSED; if (SOCKET_ERROR == send(clifd, (char*)&exitmsg, sizeof(struct MsgHeader), 0)) //失败发送给客户端 { printf("send faild: %d\n", WSAGetLastError()); } printf("完成!\n"); closeSocket(clifd); return false; break; } printf("%s\n", g_recvBuf); return true; } /* *1.客户端请求下载文件 —把文件名发送给服务器 *2.服务器接收客户端发送的文件名 —根据文件名找到文件,把文件大小发送给客户端 *3.客户端接收到文件大小—准备开始接受,开辟内存 准备完成要告诉服务器可以发送了 *4.服务器接受的开始发送的指令开始发送 *5.开始接收数据,存起来 接受完成,告诉服务器接收完成 *6.关闭连接 */ bool readFile(SOCKET clifd, struct MsgHeader* pmsg) { FILE* pread = fopen(pmsg->fileInfo.fileName, "rb"); if (pread == NULL) { printf("找不到[%s]文件...\n", pmsg->fileInfo.fileName); struct MsgHeader msg; msg.msgID = MSG_OPENFILE_FAILD; // MSG_OPENFILE_FAILD = 6 if (SOCKET_ERROR == send(clifd, (char*)&msg, sizeof(struct MsgHeader), 0)) // 失败发送给客户端 { printf("send faild: %d\n", WSAGetLastError()); } return false; } // 获取文件大小 fseek(pread, 0, SEEK_END); g_fileSize = ftell(pread); fseek(pread, 0, SEEK_SET); // 把文件大小发给客户端 char text[100]; char tfname[200] = { 0 }; struct MsgHeader msg; msg.msgID = MSG_FILESIZE; // MSG_FILESIZE = 2 msg.fileInfo.fileSize = g_fileSize; _splitpath(pmsg->fileInfo.fileName, NULL, NULL, tfname, text); //****************************************** strcat(tfname, text); strcpy(msg.fileInfo.fileName, tfname); send(clifd, (char*)&msg, sizeof(struct MsgHeader),0); // 文件名和后缀、文件大小发回客户端 第一次发送给客户端 //读写文件内容 g_fileBuf = calloc(g_fileSize + 1, sizeof(char)); if (g_fileBuf == NULL) { printf("内存不足,重试\n"); return false; } fread(g_fileBuf, sizeof(char), g_fileSize, pread); g_fileBuf[g_fileSize] = '\0'; fclose(pread); return true; } bool sendFile(SOCKET clifd, struct MsgHeader* pms) { struct MsgHeader msg; // 告诉客户端准备接收文件 msg.msgID = MSG_READY_READ; // 如果文件的长度大于每个数据包能传送的大小(1012),那么久分块 for (size_t i = 0; i < g_fileSize; i += PACKET_SIZE) // PACKET_SIZE = 1012 { msg.packet.nStart = i; // 包的大小大于总数据的大小 if (i + PACKET_SIZE + 1 > g_fileSize) { msg.packet.nsize = g_fileSize - i; } else { msg.packet.nsize = PACKET_SIZE; } memcpy(msg.packet.buf, g_fileBuf+msg.packet.nStart, msg.packet.nsize); if (SOCKET_ERROR == send(clifd, (char*)&msg, sizeof(struct MsgHeader), 0)) // 告诉客户端可以发送 { printf("文件发送失败:%d\n", WSAGetLastError()); } } return true; }

四、运行、运行结果

1.运行可以通过exe文件运行,或运行FTPserver后,运行FTPclient端是启动新实例。

2.运行结束后FTPclient会自行关闭。

3.文件传输到exe所在的文件夹中。

 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3